﻿using Sgr.Domain.Entities;
using Sgr.Domain.Entities.Auditing;
using Sgr.Exceptions;
using System;
using System.Linq;

namespace Sgr.UPMS.Domain.Menus
{
    /// <summary>
    /// 菜单
    /// </summary>
    public class Menu : CreationAndModifyAuditedEntity<long, long>, IAggregateRoot, IOptimisticLock, ITreeNode<long>, IHaveCode
    {
        private const int MAX_DEPTH = 5; // 最大层级深度

        /// <summary>
        /// 无参构造函数，供ORM框架使用
        /// </summary>
        private Menu()
        {
            IsActive = true;
            IsDeactivateByParent = false;
            ExternalLink = false;
            Hidden = false;
            KeepAlive = false;
            Affix = false;
            Order = 0;
        }

        /// <summary>
        /// 创建菜单
        /// </summary>
        /// <param name="name">菜单名称</param>
        /// <param name="type">菜单类型</param>
        /// <param name="code">菜单编码</param>
        /// <param name="permission">权限标识</param>
        internal Menu(
            string name,
            MenuType type,
            string code,
            string? permission = null)
        {
            ValidateName(name);
            ValidateCode(code);
            ValidatePermission(permission);

            Name = name;
            Type = type;
            Code = code;
            Permission = permission;

            IsActive = true;
            IsDeactivateByParent = false;
            ExternalLink = false;
            Hidden = false;
            KeepAlive = false;
            Affix = false;
            Order = 0;
        }

        #region 属性

        /// <summary>
        /// 菜单名称
        /// </summary>
        public string Name { get; private set; } = string.Empty;

        /// <summary>
        /// 菜单编码
        /// </summary>
        public string Code { get; private set; } = string.Empty;

        /// <summary>
        /// 菜单图标
        /// </summary>
        public string? Icon { get; private set; }

        /// <summary>
        /// 路由名称
        /// </summary>
        public string? RouteName { get; private set; }

        /// <summary>
        /// 路由路径(用于路由匹配，页面组件与布局的关联通常通过路由嵌套实现)
        /// </summary>
        public string? RoutePath { get; private set; }

        /// <summary>
        /// 组件路径（用于路由渲染，决定由哪个VUE文件来渲染，页面组件与布局的关联通常通过路由嵌套实现）
        /// </summary>
        public string? ComponentPath { get; private set; }

        /// <summary>
        /// 重定向路径
        /// </summary>
        public string? RedirectPath { get; private set; }

        /// <summary>
        /// 排序顺序
        /// </summary>
        public int Order { get; private set; }

        /// <summary>
        /// 权限标识
        /// </summary>
        public string? Permission { get; private set; }

        /// <summary>
        /// 菜单类型
        /// </summary>
        public MenuType Type { get; private set; }

        /// <summary>
        /// 是否激活
        /// </summary>
        public bool IsActive { get; private set; }

        /// <summary>
        /// 是否因父菜单停用而停用
        /// </summary>
        public bool IsDeactivateByParent { get; private set; }

        /// <summary>
        /// 描述
        /// </summary>
        public string? Description { get; private set; }

        /// <summary>
        /// 是否外部链接
        /// </summary>
        public bool ExternalLink { get; private set; }

        /// <summary>
        /// 是否隐藏
        /// </summary>
        public bool Hidden { get; private set; }

        /// <summary>
        /// 是否缓存
        /// </summary>
        public bool KeepAlive { get; private set; }

        /// <summary>
        /// 目标窗口
        /// </summary>
        public TargetType? Target { get; private set; }

        /// <summary>
        /// 是否固定
        /// </summary>
        public bool Affix { get; private set; }

        #endregion 属性

        #region 验证方法

        /// <summary>
        /// 验证名称
        /// </summary>
        /// <param name="name">菜单名称</param>
        private static void ValidateName(string name)
        {
            if (string.IsNullOrWhiteSpace(name))
                throw DomainException.ValidationError("菜单名称不能为空");

            if (name.Length > 255)
                throw DomainException.ValidationError("菜单名称长度不能超过255个字符");
        }

        /// <summary>
        /// 验证编码
        /// </summary>
        /// <param name="code">菜单编码</param>
        private static void ValidateCode(string code)
        {
            if (string.IsNullOrWhiteSpace(code))
                throw DomainException.ValidationError("菜单编码不能为空");

            if (code.Length > 255)
                throw DomainException.ValidationError("菜单编码长度不能超过255个字符");
        }

        /// <summary>
        /// 验证权限标识
        /// </summary>
        /// <param name="permission">权限标识</param>
        private static void ValidatePermission(string? permission)
        {
            if (!string.IsNullOrEmpty(permission) && permission.Length > 255)
                throw DomainException.ValidationError("权限标识长度不能超过255个字符");
        }

        /// <summary>
        /// 验证路由
        /// </summary>
        private void ValidateRoute(string? routeName, string? routePath, string? componentPath, string? redirectPath)
        {
            if (!string.IsNullOrEmpty(routeName) && routeName.Length > 255)
                throw DomainException.ValidationError("路由名称长度不能超过255个字符");

            if (!string.IsNullOrEmpty(routePath) && routePath.Length > 255)
                throw DomainException.ValidationError("路由路径长度不能超过255个字符");

            if (!string.IsNullOrEmpty(componentPath) && componentPath.Length > 255)
                throw DomainException.ValidationError("组件路径长度不能超过255个字符");

            if (ExternalLink && !routePath?.StartsWith("http", StringComparison.OrdinalIgnoreCase) == true)
                throw DomainException.ValidationError("外部链接必须以http或https开头");

            if (!string.IsNullOrEmpty(redirectPath) && redirectPath.Length > 255)
                throw DomainException.ValidationError("重定向路径长度不能超过255个字符");
        }

        /// <summary>
        /// 验证节点路径
        /// </summary>
        private static void ValidateNodePath(string nodePath)
        {
            if (string.IsNullOrEmpty(nodePath))
                throw DomainException.ValidationError("节点路径不能为空");
        }

        #endregion 验证方法

        #region 业务方法

        /// <summary>
        /// 更新基本信息
        /// </summary>
        /// <param name="name">菜单名称 (必填,最大长度255)</param>
        /// <param name="icon">菜单图标 (可选)</param>
        /// <param name="routeName">路由名称 (可选,最大长度255)</param>
        /// <param name="routePath">路由路径 (可选,最大长度255,外部链接时必须以http/https开头)</param>
        /// <param name="componentPath">组件路径 (可选,最大长度255)</param>
        /// <param name="redirectPath">重定向 (可选,最大长度255)</param>
        /// <param name="permission">权限标识 (可选,最大长度255)</param>
        /// <param name="description">描述信息 (可选)</param>
        /// <param name="order">排序号 (可选)</param>
        /// <exception cref="DomainException">当验证失败时抛出</exception>
        public void UpdateBasicInfo(
            string name,
            string? icon = null,
            string? routeName = null,
            string? routePath = null,
            string? componentPath = null,
            string? redirectPath = null,
            string? permission = null,
            string? description = null,
            int? order = null)
        {
            ValidateName(name);
            ValidatePermission(permission);
            ValidateRoute(routeName, routePath, componentPath, redirectPath);

            Name = name;
            Icon = icon;
            RouteName = routeName;
            RoutePath = routePath;
            ComponentPath = componentPath;
            RedirectPath = redirectPath;
            Permission = permission;
            Description = description;

            if (order.HasValue)
                Order = order.Value;
        }

        /// <summary>
        /// 设置菜单配置
        /// </summary>
        /// <param name="externalLink">是否为外部链接 (当为true时,routePath必须以http/https开头)</param>
        /// <param name="hidden">是否在菜单中隐藏</param>
        /// <param name="keepAlive">是否启用页面缓存</param>
        /// <param name="affix">是否固定在标签栏</param>
        /// <param name="target">打开方式 (可选,默认当前窗口)</param>
        /// <exception cref="DomainException">当外部链接验证失败时抛出</exception>
        public void UpdateConfig(
            bool externalLink,
            bool hidden,
            bool keepAlive,
            bool affix,
            TargetType? target = null)
        {
            if (externalLink && !string.IsNullOrEmpty(RoutePath))
                ValidateRoute(RouteName, RoutePath, ComponentPath, RedirectPath);

            ExternalLink = externalLink;
            Hidden = hidden;
            KeepAlive = keepAlive;
            Affix = affix;
            Target = target;
        }

        /// <summary>
        /// 设置父级菜单
        /// </summary>
        /// <param name="parentId">父级菜单ID</param>
        /// <param name="nodePath">节点路径</param>
        /// <exception cref="DomainException">当层级超过最大深度时抛出异常</exception>
        internal void SetParent(long parentId, string nodePath)
        {
            // 验证层级是否超过最大深度
            ValidateNodePath(nodePath);

            // 检查路径深度
            var depth = nodePath.Count(c => c == '#') + 1;
            if (depth > MAX_DEPTH)
            {
                throw DomainException.ValidationError($"菜单层级不能超过{MAX_DEPTH}层");
            }

            NodePath = nodePath;

            if (ParentId != parentId)
            {
                ParentId = parentId;
            }
        }

        /// <summary>
        /// 修改菜单编码
        /// </summary>
        /// <param name="code"></param>
        internal void ChangeCode(string code)
        {
            ValidateCode(code);
            if (Code != code)
            {
                Code = code;
            }
        }

        /// <summary>
        /// 激活菜单
        /// </summary>
        public void Activate()
        {
            if (!IsActive)
            {
                IsActive = true;
                IsDeactivateByParent = false;
            }
        }

        /// <summary>
        /// 停用菜单
        /// </summary>
        /// <param name="byParent">是否因父菜单停用而停用</param>
        public void Deactivate(bool byParent = false)
        {
            if (IsActive)
            {
                IsActive = false;
                IsDeactivateByParent = byParent;
            }
        }

        /// <summary>
        /// 恢复因父菜单停用而停用的菜单
        /// </summary>
        /// <remarks>
        /// 该方法只会恢复那些因为父菜单停用而被停用的菜单。
        /// 如果菜单是手动停用的（IsDeactivateByParent = false），则不会被恢复。
        /// </remarks>
        public void RestoreIfDeactivatedByParent()
        {
            if (!IsActive && IsDeactivateByParent)
            {
                IsActive = true;
                IsDeactivateByParent = false;
            }
        }

        /// <summary>
        /// 是否为顶级菜单
        /// </summary>
        public bool IsRoot()
        {
            return ParentId == 0;
        }

        #endregion 业务方法

        #region ITreeNode (树形结构)

        /// <summary>
        /// 上级菜单Id
        /// </summary>
        public long ParentId { get; private set; }

        /// <summary>
        /// 树节点层次目录
        /// </summary>
        public string NodePath { get; private set; } = string.Empty;

        #endregion ITreeNode (树形结构)

        #region IOptimisticLock (乐观锁)

        /// <summary>
        /// 行版本
        /// </summary>
        public long RowVersion { get; set; }

        #endregion IOptimisticLock (乐观锁)
    }
}